/*
  Instances of this class determine the speed of an object.
  To do that, fuzzy rules learned by a machine learning algorithm are used. It is also needed a file where
  fuzzy variables and fuzzy sets are specified.

*/
import java.util.Vector;
import java.util.StringTokenizer;
import java.io.*;
import java.sql.*;

class SpeedAnalyzer{
	
    protected Vector<FuzzyClassSA> v_classes;
    protected Vector<FuzzyVariableSA> v_variables;	
    protected Vector<FuzzyObjectSA> v_objects; //objects used in the classification process.
    
    public SpeedAnalyzer(){
	v_variables = new Vector();
	v_classes = new Vector();
	v_objects = new Vector();
    }

    public Vector<FuzzyClassSA> getClasses(){return this.v_classes;}
	

    //loadVariables: load the information regarding the variables from a input file.
    public void loadVariables(String file){
	FileReader f_variables;
       
	try{
	    f_variables = new FileReader(file);
	    BufferedReader bf = new BufferedReader(f_variables);
	    String sLine;

	    while ((sLine = bf.readLine())!=null) {
		if(sLine.charAt(0)=='#'){
		    //the name of a variable
		    FuzzyVariableSA fv = new FuzzyVariableSA(sLine.substring(1,sLine.length()));
		    this.v_variables.add(fv);
		}else{
		    //including fuzzy sets on the variables.
		    StringTokenizer st = new StringTokenizer(sLine, ":");
		    String label = st.nextToken();
		    String values = st.nextToken();
		    st = new StringTokenizer(values, ",");
		    float a = Float.valueOf(st.nextToken());
		    float b = Float.valueOf(st.nextToken());
		    float c = Float.valueOf(st.nextToken());
		    float d = Float.valueOf(st.nextToken());

		    FuzzySetSA fs = new FuzzySetSA(label,a,b,c,d);
		    FuzzyVariableSA fv = this.v_variables.elementAt(this.v_variables.size()-1);
		    fv.addFuzzySet(fs);
		}
	    }//end while
	}catch(Exception e){
	    System.out.println("Error while loading variables information: "+e);
	}//end catch	
    }//end loadVariables
    
    //print the variables and their values on the screen
    public void printVariables(){
	
	for(int i=0; i< this.v_variables.size();i++){
	    FuzzyVariableSA fv = this.v_variables.elementAt(i);
	    System.out.println(fv.getName());
	    for(int j=0; j< fv.getNumberFuzzySets();j++){
		FuzzySetSA fs = fv.getFuzzySetAt(j);
		System.out.println(fs.getLabel()+":"+fs.getA()+","+fs.getB()+","+fs.getC()+","+fs.getD());
	    }//for
	}//end for
    }//end printVariables

    //loadRules: loadVariables must be called before this method. Variables are needed to build the set of rules.
    public void loadRules(String file){
	FileReader f_rules;
        int num_rules = 1;

	try{
	    f_rules = new FileReader(file);
	    BufferedReader bf = new BufferedReader(f_rules);
	    String sLine;

	    while ((sLine = bf.readLine())!=null) {
		if(sLine.charAt(0)=='#'){
		    //the name of a variable
		    FuzzyClassSA fc = new FuzzyClassSA(sLine.substring(1,sLine.length()));
		    this.v_classes.add(fc);
		}else{
		    //including  fuzzy rules on classes.
		    StringTokenizer st = new StringTokenizer(sLine, ";");
		    int number_variable = 0;
		    FuzzyRuleSA fr = new FuzzyRuleSA("v"+num_rules);
		    num_rules++;
		    
		    while(st.hasMoreTokens()){
			//every token corresponds with a variable
			String rule_fragment = st.nextToken();
			FuzzyVariableSA fv = new FuzzyVariableSA(((FuzzyVariableSA)this.v_variables.elementAt(number_variable)).getName());
			if(rule_fragment.charAt(0)=='{'){
			    //all fuzzy sets of the variable are included in the rule.
			    fr.addVariable(fv);
			}else{
			    StringTokenizer st2 = new StringTokenizer(rule_fragment, "|");
			    //Every token is a fuzzy set of the current variable
			    while (st2.hasMoreTokens()){
				String s_fuzzy_sets = st2.nextToken();
				//every token is a tuple such as (a,b,c,d)
				s_fuzzy_sets = s_fuzzy_sets.substring(1,s_fuzzy_sets.length()-1);
				StringTokenizer st3 = new StringTokenizer(s_fuzzy_sets, ",");
				
				float a = Float.valueOf(st3.nextToken());
				float b = Float.valueOf(st3.nextToken());
				float c = Float.valueOf(st3.nextToken());
				float d = Float.valueOf(st3.nextToken());

				FuzzySetSA fs = new FuzzySetSA("",a,b,c,d);
				fv.addFuzzySet(fs);				
			    }//end while
			    fr.addVariable(fv);
			}
			number_variable++;
		    }//end while
		    FuzzyClassSA fc = this.v_classes.elementAt(v_classes.size()-1);
		    fc.addRule(fr);
		}//end else 
	    }//end while
	}catch(Exception e){
	    System.out.println("Error while loading rules on the classes: "+e);
	}//end catch	
    }//end loadVariables

    public void printRules(){
	System.out.println("-----------RULES-----------");
	for(int i=0;i< this.v_classes.size();i++){
	    FuzzyClassSA fc = this.v_classes.elementAt(i);
	    System.out.println("#"+fc.getName());
	    for(int j=0;j<fc.numRules();j++){
	       
		FuzzyRuleSA fr = fc.getRuleAt(j);
		for(int w=0; w < this.v_variables.size();w++){
		    FuzzyVariableSA fv = fr.getFuzzyVariableAt(w);
		    if(fv.getNumberFuzzySets() == 0)
			System.out.print("{_};");
		    else{
			for(int z=0;z<fv.getNumberFuzzySets();z++){
			    FuzzySetSA fs = fv.getFuzzySetAt(z);
			    System.out.print("("+fs.getA()+","+fs.getB()+","+fs.getC()+","+fs.getD()+")");
			}//end for - while there are fuzzy sets
			System.out.print(";");
		    }
		}//end for, while there are variables 
		System.out.println("");
	    }//end for - while there are rules
	}//end for - while there are classes
    }//end printRules


    public FuzzyObjectSA classify(int frame, String id, float nclass, float x, float y){
	//this method classify one object using the learned rules and earlier classifications (tracking is taken into
	//account to classify objects).
	
	FuzzyObjectSA fo = null;
	for(int i=0;i<this.v_objects.size();i++){
	    FuzzyObjectSA tmp = v_objects.elementAt(i);
	    if((tmp.getId()).equals(id))
		fo = tmp;
	}//end for		
	if(fo == null){
	    fo = new FuzzyObjectSA(id);
	    this.v_objects.add(fo);
	}

	//how object belongs to the classes.
	Vector v_values = fo.getValues();
	int last_frame = fo.getLastFrame();

	if((last_frame==-1) || (frame - last_frame) >= 25){
	    fo.erase();
	    fo.setLastFrame(frame);
	    
	    
	    if(last_frame == -1){
		v_values.addElement(x);
		v_values.addElement(y);
		v_values.addElement(nclass);
		v_values.addElement(new Float(0));
		v_values.addElement(new Float(0));
		v_values.addElement(new Float(0));
	    }else{		
		v_values.addElement(x);
		v_values.addElement(y);
		v_values.addElement(nclass);
		fo.last_hd = Math.abs(x-fo.getLastX());		
		fo.last_vd = Math.abs(y-fo.getLastY());
		fo.last_mov = (float)Math.sqrt(Math.pow(fo.last_hd,2)+Math.pow(fo.last_vd,2));
		v_values.addElement(fo.last_hd);
		v_values.addElement(fo.last_vd);
		v_values.addElement(fo.last_mov);		
	    }
	    fo.setLastX((int)x);
	    fo.setLastY((int)y);
	    
	    for(int i=0;i < this.v_classes.size();i++){
		FuzzyClassSA fc = this.v_classes.elementAt(i);
		float membership_class = fc.membershipValue(fo);	    
		
		//updating v_objects
		
		if(membership_class > 0){	    
		    MembershipSA mship = (MembershipSA)fo.getMembershipByClass(fc.getName());
		    
		    if(mship == null){
			mship = new MembershipSA(fc.getName());		    
			fo.addMembership(mship);		    
		    }
		    mship.setMembershipValue(membership_class);		  
		}//end if	    
	    }//end for
	}//end if

	fo.updateNormalityDegree();

	return fo;
    }//end classify

    
    
}//end class
